'use strict';
(function(){
  const $ = (id) => document.getElementById(id);
  const bridge = window.AndroidOverlay || window.OverlayBridge || {};

  // 引入Capacitor
  const Capacitor = window.Capacitor;

  // 根元素（用于挂载进场动画）
  const rootEl = $('overlay-root');

  const inputEl = $('input');
  const tabsRow = $('tabs-row');
  const subcatRow = $('subcat-row');
  const sendBtn = $('send-btn');
  const pasteBtn = $('paste-btn');
  const closeBtn = $('close-btn');
  const candidatesEl = $('candidates');
  const shuffleBtn = $('shuffle-btn');
  const creditBadge = $('credit-badge');
  const srLive = $('sr-live');
  const modelSelect = $('model-select');
  const dragBar = $('drag-bar');
  const titleActions = $('title-actions');
  const overlayTitle = $('overlay-title');
  const prefersReduced = (() => {
    try { return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; } catch(_) { return false; }
  })();

  // idleCallback 兜底
  const rIC = window.requestIdleCallback || function(cb){ return setTimeout(() => cb({ timeRemaining: () => 0, didTimeout: true }), 16); };
  const cIC = window.cancelIdleCallback || clearTimeout;
  let genTimer = null; let genIdle = null;

  let categoriesTree = [];
  let selectedCategory = null; // object
  let selectedSubcategory = null; // object
  let lastGenerated = [];
  let defaults = {};
  let isLoading = false;
  let lastError = null;
  // 单次请求模式：由原生返回一条包含 '||' 分隔的字符串，前端拆分为多条候选
  let aiConfig = { provider: '', model: 'gpt-4o-mini', apiKey: '', baseUrl: '' };

  function safeJsonParse(s, fallback) {
    try { return s ? JSON.parse(s) : (fallback ?? {}); } catch { return fallback ?? {}; }
  }

  function maskKey(k) {
    if (!k) return '';
    return k.length <= 8 ? '****' : (k.slice(0,4) + '...' + k.slice(-2));
  }

  function loadAiConfigFromBridge() {
    try {
      if (bridge.getAiConfig) {
        const raw = bridge.getAiConfig();
        const cfg = safeJsonParse(raw, {});
        // do not log apiKey
        try {
          const snap = { ...cfg };
          if (snap.apiKey) snap.apiKey = maskKey(String(snap.apiKey));
          console.log('[Overlay] getAiConfig', snap);
        } catch(_) {}
        return cfg;
      }
    } catch(_) {}
    return {};
  }

  function providerKey(p) {
    const s = String(p || '').toLowerCase();
    if (s.includes('google') || s.includes('gemini')) return 'gemini';
    return 'openai';
  }

  function defaultModelsForProvider(p) {
    const key = providerKey(p);
    if (key === 'gemini') {
      return [
        { label: 'gemini-1.5-flash', value: 'gemini-1.5-flash' },
        { label: 'gemini-1.5-pro', value: 'gemini-1.5-pro' },
        { label: 'gemini-1.5-flash-8b', value: 'gemini-1.5-flash-8b' }
      ];
    }
    // openai-compatible
    return [
      { label: 'gpt-4o-mini', value: 'gpt-4o-mini' },
      { label: 'gpt-4o', value: 'gpt-4o' },
      { label: 'o3-mini', value: 'o3-mini' }
    ];
  }

  function populateModelOptions(currentProvider, currentModel) {
    if (!modelSelect) return;
    const models = defaultModelsForProvider(currentProvider).slice();
    const exists = models.some(m => m.value === currentModel);
    if (!exists && currentModel) models.unshift({ label: currentModel, value: currentModel });
    modelSelect.innerHTML = '';
    for (const m of models) {
      const opt = document.createElement('option');
      opt.value = m.value; opt.textContent = m.label;
      modelSelect.appendChild(opt);
    }
    if (currentModel) modelSelect.value = currentModel;
  }

  // In-page custom dropdown to avoid native <select> popup issues under system overlay
  let modelTrigger = null;
  let modelMenu = null;
  let modelMenuOpen = false;

  function closeModelMenu() {
    if (modelMenuOpen && modelMenu) {
      modelMenu.style.display = 'none';
      modelMenuOpen = false;
    }
  }

  function openModelMenu() {
    if (!modelMenu) return;
    modelMenu.style.display = 'block';
    modelMenuOpen = true;
  }

  function rebuildModelMenu(currentProvider, currentModel) {
    if (!titleActions) return;
    if (!modelTrigger) {
      modelTrigger = document.createElement('button');
      modelTrigger.id = 'model-trigger';
      modelTrigger.type = 'button';
      modelTrigger.setAttribute('aria-haspopup', 'listbox');
      modelTrigger.setAttribute('aria-expanded', 'false');
      titleActions.appendChild(modelTrigger);
      modelTrigger.addEventListener('click', (e) => {
        e.stopPropagation();
        if (modelMenuOpen) { closeModelMenu(); modelTrigger.setAttribute('aria-expanded', 'false'); }
        else { openModelMenu(); modelTrigger.setAttribute('aria-expanded', 'true'); }
      });
      // Close when clicking elsewhere
      document.addEventListener('mousedown', (e) => {
        if (!modelMenuOpen) return;
        if (modelMenu && !modelMenu.contains(e.target) && e.target !== modelTrigger) {
          closeModelMenu();
          modelTrigger?.setAttribute('aria-expanded', 'false');
        }
      });
    }
    if (!modelMenu) {
      modelMenu = document.createElement('div');
      modelMenu.id = 'model-menu';
      modelMenu.setAttribute('role', 'listbox');
      titleActions.appendChild(modelMenu);
    }
    // Populate menu
    const models = defaultModelsForProvider(currentProvider).slice();
    const exists = models.some(m => m.value === currentModel);
    if (!exists && currentModel) models.unshift({ label: currentModel, value: currentModel });
    modelMenu.innerHTML = '';
    for (const m of models) {
      const item = document.createElement('div');
      item.className = 'model-item' + (m.value === currentModel ? ' active' : '');
      item.setAttribute('role', 'option');
      item.setAttribute('data-value', m.value);
      item.textContent = m.label;
      item.addEventListener('click', (e) => {
        e.stopPropagation();
        try {
          aiConfig.model = m.value;
          // reflect into hidden select for consistency
          if (modelSelect) modelSelect.value = m.value;
          // persist
          if (bridge.setAiConfig) {
            const toSave = { ...aiConfig };
            try { const snap = { ...toSave }; if (snap.apiKey) snap.apiKey = maskKey(String(snap.apiKey)); console.log('[Overlay] setAiConfig (model menu)', snap); } catch(_) {}
            bridge.setAiConfig(JSON.stringify(toSave));
          }
          // update UI
          modelTrigger.textContent = m.label;
          rebuildModelMenu(aiConfig.provider, aiConfig.model);
          closeModelMenu();
          modelTrigger.setAttribute('aria-expanded', 'false');
          // auto-regenerate if input exists
          if (inputEl && inputEl.value && inputEl.value.trim()) {
            scheduleGenerate(true);
          }
        } catch (err) {
          console.warn('[Overlay] model menu apply failed', err);
        }
      });
      modelMenu.appendChild(item);
    }
    // Trigger label
    modelTrigger.textContent = (models.find(m => m.value === currentModel)?.label) || currentModel || '选择模型';
    closeModelMenu();
  }

  function initModelSelector() {
    if (!modelSelect) return;
    aiConfig = { ...aiConfig, ...loadAiConfigFromBridge() };
    populateModelOptions(aiConfig.provider, aiConfig.model);
    // Hide native select and use in-page menu to avoid popup issues under system overlay
    try { modelSelect.style.position = 'absolute'; modelSelect.style.width = '1px'; modelSelect.style.height = '1px'; modelSelect.style.opacity = '0'; modelSelect.style.pointerEvents = 'none'; } catch(_) {}
    rebuildModelMenu(aiConfig.provider, aiConfig.model);
    modelSelect.addEventListener('change', () => {
      try {
        const val = modelSelect.value;
        aiConfig.model = val;
        if (bridge.setAiConfig) {
          const toSave = { ...aiConfig };
          // never log apiKey
          try {
            const snap = { ...toSave }; if (snap.apiKey) snap.apiKey = maskKey(String(snap.apiKey));
            console.log('[Overlay] setAiConfig (model change)', snap);
          } catch(_) {}
          bridge.setAiConfig(JSON.stringify(toSave));
        }
        // if user already typed something, re-generate with new model
        if (inputEl && inputEl.value && inputEl.value.trim()) {
          scheduleGenerate(true);
        }
      } catch (e) {
        console.warn('[Overlay] model change failed', e);
      }
    });
  }

  // Dynamically inform native drag handle about left/right exclusion widths (in physical px)
  function updateDragExclusions() {
    try {
      if (!bridge || (!bridge.setDragExclusionLeft && !bridge.setDragExclusionRight)) return;
      if (!dragBar) return;
      const dpr = Math.max(1, window.devicePixelRatio || 1);
      const barRect = dragBar.getBoundingClientRect();
      // Left exclusion: only cover the left cluster (grip + title)
      let leftCss = 0;
      const gripEl = (() => { try { return dragBar.querySelector('.grip'); } catch(_) { return null; } })();
      const gripRight = (() => { try { return gripEl ? gripEl.getBoundingClientRect().right : barRect.left; } catch(_) { return barRect.left; } })();
      const titleRight = (() => { try { return overlayTitle ? overlayTitle.getBoundingClientRect().right : barRect.left; } catch(_) { return barRect.left; } })();
      const leftEdge = Math.max(gripRight, titleRight);
      leftCss = Math.max(0, Math.ceil(leftEdge - barRect.left));
      if (bridge.setDragExclusionLeft) {
        const leftPx = Math.max(0, Math.round(leftCss * dpr));
        try { bridge.setDragExclusionLeft(leftPx); } catch(_) {}
      }
      // Right exclusion: from the leftmost of (title-actions, close button) to bar right
      let rightCss = 0;
      const taLeft = (() => { try { return titleActions ? titleActions.getBoundingClientRect().left : barRect.right; } catch(_) { return barRect.right; } })();
      const cbLeft = (() => { try { return closeBtn ? closeBtn.getBoundingClientRect().left : barRect.right; } catch(_) { return barRect.right; } })();
      const rightStart = Math.min(taLeft, cbLeft);
      rightCss = Math.max(0, Math.ceil(barRect.right - rightStart));
      if (!rightCss) { rightCss = 64; } // fallback in CSS px
      if (bridge.setDragExclusionRight) {
        const rightPx = Math.max(0, Math.round(rightCss * dpr));
        try { bridge.setDragExclusionRight(rightPx); } catch(_) {}
      }
      try {
        const barW = Math.round(barRect.width);
        const centerCss = Math.max(0, barW - leftCss - rightCss);
        console.log('[Overlay] drag exclusions ' + JSON.stringify({ leftCss, rightCss, dpr, barW, centerCss }));
      } catch(_) {}
    } catch(_) {}
  }

  // 初始状态（如分类/历史/位置）
  try {
    if (bridge.getInitialState) {
      const init = JSON.parse(bridge.getInitialState());
      // clipboard prefill
      if (init && typeof init.clipboard === 'string' && init.clipboard.length && inputEl && !inputEl.value.trim()) {
        inputEl.value = init.clipboard;
      }
    }
  } catch (e) {}

  // 从本地配置文件加载分类（categories.json），容忍 // 注释
  // 添加调试日志验证window.categories是否可用
  console.log('Checking window.categories availability:', window.categories);
  loadCategoriesFromFile();
  // Initialize model selector after DOM is ready
  initModelSelector();
  // Apply initial drag exclusion calculation after layout
  setTimeout(updateDragExclusions, 0);
  window.addEventListener('resize', () => { setTimeout(updateDragExclusions, 0); });
  window.addEventListener('orientationchange', () => { setTimeout(updateDragExclusions, 100); });
  // Observe changes that might affect widths (e.g., model options, fonts)
  try {
    const mo = new MutationObserver(() => { setTimeout(updateDragExclusions, 0); });
    if (titleActions) mo.observe(titleActions, { childList: true, subtree: true, attributes: true });
  } catch(_) {}

  function stripJsonComments(txt) {
    // 去除 //... 到行尾（不处理 /* */ 多行注释）
    return txt.replace(/(^|\s)\/\/.*$/gm, '');
  }

  function slugify(s) {
    return (s || '').toString().trim().toLowerCase().replace(/\s+/g,'-').replace(/[^a-z0-9-_]/g,'').slice(0,32) || null;
  }

  function normalizeSchema(raw) {
    const out = { defaults: raw?.defaults || {}, categories: [] };
    const cats = Array.isArray(raw) ? raw : (Array.isArray(raw?.categories) ? raw.categories : []);
    const seen = new Set();
    cats.forEach((c, idx) => {
      if (!c || typeof c !== 'object') return;
      let id = c.id && String(c.id).trim();
      if (!id) id = slugify(c.name) || `cat_${idx+1}`;
      while (seen.has(id)) id = id + '_x';
      seen.add(id);
      const layout = c.layout || (Array.isArray(c.subcategories) ? 'cards' : (Array.isArray(c.items) ? 'list' : 'single'));
      const subs = Array.isArray(c.subcategories) ? c.subcategories.map((s, j) => {
        if (!s || typeof s !== 'object') return null;
        let sid = s.id && String(s.id).trim();
        if (!sid) sid = slugify(s.name) || `${id}_sub_${j+1}`;
        return {
          id: sid,
          name: s.name || `项${j+1}`,
          icon: s.icon,
          subtitle: s.subtitle || s.desc || s.description,
          // prefer single prompt string; keep legacy prompts[] for compatibility
          prompt: typeof s.prompt === 'string' ? s.prompt : undefined,
          prompts: Array.isArray(s.prompts) ? s.prompts : [],
          presets: s.presets || []
        };
      }).filter(Boolean) : [];
      out.categories.push({
        id,
        name: c.name || id,
        layout,
        subcategories: subs,
        items: Array.isArray(c.items) ? c.items : undefined,
        pageSize: typeof c.pageSize === 'number' ? c.pageSize : undefined,
        shuffle: c.shuffle === true,
        itemSize: c.itemSize || undefined,
        notes: c.notes || undefined,
        // allow category-level prompt(s) too, though subcategory usually provides it
        prompt: typeof c.prompt === 'string' ? c.prompt : undefined,
        prompts: Array.isArray(c.prompts) ? c.prompts : undefined,
      });
    });
    return out;
  }

  // 监听分类加载完成事件
  function loadCategoriesFromFile() {
    return new Promise((resolve) => {
      // 如果分类已经加载完成，直接初始化
      if (window.categories && window.categories.categories) {
        const norm = normalizeSchema(window.categories);
        categoriesTree = norm.categories;
        defaults = norm.defaults || {};
        renderCategories(categoriesTree);
        resolve();
        return;
      }

      // 否则等待分类加载完成事件
      const onCategoriesLoaded = (e) => {
        try {
          const norm = normalizeSchema(window.categories);
          categoriesTree = norm.categories;
          defaults = norm.defaults || {};
          renderCategories(categoriesTree);
          resolve();
        } catch (err) {
          console.error('Failed to load categories', err);
          resolve();
        } finally {
          // 清理事件监听
          document.removeEventListener('categories:loaded', onCategoriesLoaded);
        }
      };

      // 设置超时，防止一直等待
      const timeout = setTimeout(() => {
        console.error('Categories loading timeout');
        document.removeEventListener('categories:loaded', onCategoriesLoaded);
        resolve();
      }, 5000);

      // 添加事件监听
      document.addEventListener('categories:loaded', onCategoriesLoaded, { once: true });
    });
  }


  function renderCategories(list) {
    if (!tabsRow) return;
    tabsRow.innerHTML = '';
    list.forEach(cat => {
      const btn = document.createElement('button');
      btn.className = 'chip';
      // 一级选项：仅文字（图标不显示）
      btn.textContent = (cat.name || '');
      btn.addEventListener('click', () => {
        selectedCategory = cat;
        selectedSubcategory = null;
        updateActiveStates();
        renderCategoryContent(cat);
      });
      tabsRow.appendChild(btn);
    });
    // 默认选中第一个分类
    if (list.length && !selectedCategory) {
      const pickId = defaults?.categoryId;
      selectedCategory = pickId ? (list.find(c => c.id === pickId) || list[0]) : list[0];
      updateActiveStates();
      renderCategoryContent(selectedCategory);
    } else {
      updateActiveStates();
    }
  }

  function renderCategoryContent(cat) {
    if (!subcatRow) return;
    subcatRow.innerHTML = '';

    // 重置subcatRow的样式
    subcatRow.classList.remove('grid');
    subcatRow.style.flexDirection = '';
    subcatRow.style.display = '';

    // 控制“换一换”显示（仅 list/grid 且可打乱时）
    const canShuffle = (cat.layout === 'list' || cat.layout === 'grid') && cat.shuffle && Array.isArray(cat.items) && cat.items.length > (cat.pageSize || 6);
    if (shuffleBtn) shuffleBtn.style.display = canShuffle ? '' : 'none';
    if (shuffleBtn) {
      shuffleBtn.onclick = () => {
        if (cat.layout === 'list' || cat.layout === 'grid') renderItems(cat, true);
      };
    }

    switch (cat.layout) {
      case 'single':
        subcatRow.style.display = 'none';
        selectedSubcategory = null;
        break;
      case 'row': {
        subcatRow.style.display = '';
        const subs = Array.isArray(cat.subcategories) ? cat.subcategories : [];
        subs.forEach(sub => {
          const chip = document.createElement('button');
          chip.className = 'chip sub';
          chip.textContent = sub.name || '';
          chip.addEventListener('click', () => { selectedSubcategory = sub; updateActiveStates(); });
          subcatRow.appendChild(chip);
        });
        if (!selectedSubcategory && subs.length) { selectedSubcategory = subs[0]; updateActiveStates(); }
        break;
      }
      case 'cards': default: {
        subcatRow.style.display = '';
        const subs = Array.isArray(cat.subcategories) ? cat.subcategories : [];
        subs.forEach(sub => {
          const card = document.createElement('div');
          card.className = 'subcard';
          const head = document.createElement('div'); head.className = 'head';
          const icon = document.createElement('div'); icon.className = 'icon'; icon.textContent = sub.icon || '✨';
          const title = document.createElement('div'); title.className = 'title'; title.textContent = sub.name || '';
          head.appendChild(icon); head.appendChild(title);
          const desc = document.createElement('div'); desc.className = 'desc'; desc.textContent = sub.subtitle || sub.desc || sub.description || '';
          const cta = document.createElement('button'); cta.type = 'button'; cta.className = 'cta icon-btn i-generate';
          cta.addEventListener('click', (e) => { e.preventDefault(); selectedSubcategory = sub; updateActiveStates(); maybeGenerate(); });
          card.addEventListener('click', () => { selectedSubcategory = sub; updateActiveStates(); });
          card.appendChild(head); if (desc.textContent) card.appendChild(desc); card.appendChild(cta);
          subcatRow.appendChild(card);
        });
        if (!selectedSubcategory && subs.length) { selectedSubcategory = subs[0]; updateActiveStates(); }
        break;
      }
      case 'list':
      case 'grid':
        subcatRow.style.display = '';
        renderItems(cat, false);
        break;
    }
  }

  function renderItems(cat, shuffle) {
    if (!subcatRow) return;
    subcatRow.innerHTML = '';
    // 保存当前是否为grid布局
    const wasGrid = subcatRow.classList.contains('grid');
    // reset container state
    subcatRow.classList.remove('grid');
    subcatRow.style.flexDirection = '';
    const items = Array.isArray(cat.items) ? cat.items.slice() : [];
    const size = cat.pageSize || 6;
    if (shuffle) items.sort(() => Math.random() - 0.5);
    const show = items.slice(0, size);
    if (cat.layout === 'list') {
      // 纵向列表（点击填充到输入框）
      subcatRow.style.flexDirection = 'column';
      show.forEach(t => {
        const li = document.createElement('div');
        li.className = 'list-item';
        li.textContent = t;
        li.addEventListener('click', () => { if (inputEl) inputEl.value = t; });
        subcatRow.appendChild(li);
      });
    } else {
      // grid
      subcatRow.classList.add('grid');
      show.forEach(t => {
        const gi = document.createElement('div');
        gi.className = `grid-item ${cat.itemSize || 'm'}`;
        gi.textContent = t;
        gi.addEventListener('click', () => { if (inputEl) inputEl.value = t; });
        subcatRow.appendChild(gi);
      });
    }
  }

  // 构造最终提示词（支持 sub.prompt 或 legacy sub.prompts[0]；支持 {input} 与 {k} 占位符）
  function buildFinalPrompt(text, k = 5) {
    let tpl = null;
    if (selectedSubcategory) {
      if (typeof selectedSubcategory.prompt === 'string') tpl = selectedSubcategory.prompt;
      else if (Array.isArray(selectedSubcategory.prompts) && selectedSubcategory.prompts.length) tpl = selectedSubcategory.prompts[0];
    }
    if (!tpl && selectedCategory) {
      if (typeof selectedCategory.prompt === 'string') tpl = selectedCategory.prompt;
      else if (Array.isArray(selectedCategory.prompts) && selectedCategory.prompts?.length) tpl = selectedCategory.prompts[0];
    }
    if (!tpl) tpl = '{input}';
    let out = String(tpl);
    // 替换占位符
    out = out.replaceAll('{input}', text);
    if (out.includes('{k}')) out = out.replaceAll('{k}', String(k));
    // 若模板未包含 {input}，则在末尾附加原始输入，确保上下文存在
    if (!tpl.includes('{input}') && text) out = out + '\n\nInput: ' + text;
    return out;
  }

  function renderCandidates(list) {
    if (!candidatesEl) return;
    candidatesEl.innerHTML = '';
    const frag = document.createDocumentFragment();
    list.forEach(item => {
      const row = document.createElement('div');
      row.className = 'candidate';
      const txt = document.createElement('div'); txt.className = 'candidate-text'; txt.textContent = item.text;
      const btn = document.createElement('button'); btn.className = 'copy-btn'; btn.textContent = '复制';
      btn.addEventListener('click', () => {
        try { bridge.copyToClipboard && bridge.copyToClipboard(item.text); } catch (_) {}
        // 轻微视觉反馈
        row.classList.add('copied');
        setTimeout(() => row.classList.remove('copied'), 600);
        // 原生反馈
        try { bridge.showToast && bridge.showToast('已复制'); } catch(_) {}
        try { bridge.hapticImpact && bridge.hapticImpact('light'); } catch(_) {}
        announce('已复制候选文本');
      });
      // 支持行内双击复制（不影响按钮）
      let lastTap = 0;
      row.addEventListener('click', (ev) => {
        if (ev.target === btn || (ev.target && btn.contains(ev.target))) return;
        const now = Date.now();
        if (now - lastTap < 280) {
          try { bridge.copyToClipboard && bridge.copyToClipboard(item.text); } catch (_) {}
          row.classList.add('copied'); setTimeout(() => row.classList.remove('copied'), 600);
          try { bridge.showToast && bridge.showToast('已复制'); } catch(_) {}
          try { bridge.hapticImpact && bridge.hapticImpact('light'); } catch(_) {}
          announce('已复制候选文本');
        }
        lastTap = now;
      });
      row.appendChild(txt); row.appendChild(btn);
      frag.appendChild(row);
    });
    candidatesEl.appendChild(frag);
  }

  function renderLoading() {
    if (!candidatesEl) return;
    candidatesEl.innerHTML = '';
    if (isLoading) {
      for (let i = 0; i < 3; i++) {
        const sk = document.createElement('div');
        sk.className = 'candidate skeleton';
        const bar = document.createElement('div'); bar.className = 'candidate-text'; bar.textContent = '生成中…';
        const btn = document.createElement('button'); btn.className = 'copy-btn'; btn.textContent = '…'; btn.disabled = true;
        sk.appendChild(bar); sk.appendChild(btn);
        candidatesEl.appendChild(sk);
      }
    } else if (lastError) {
      const er = document.createElement('div'); er.className = 'candidate error';
      const txt = document.createElement('div'); txt.className = 'candidate-text'; txt.textContent = lastError || '出错了';
      const btn = document.createElement('button'); btn.className = 'copy-btn'; btn.textContent = '重试';
      btn.addEventListener('click', () => { scheduleGenerate(true); });
      er.appendChild(txt); er.appendChild(btn); candidatesEl.appendChild(er);
    }
  }

  // Native callbacks: will be invoked from Java via evaluateJavascript
  window.__overlay_onCandidates = function(payload) {
    try {
      const arrRaw = Array.isArray(payload?.candidates) ? payload.candidates : [];
      let texts = [];
      if (arrRaw.length === 1 && typeof arrRaw[0] === 'string') {
        const s = arrRaw[0];
        if (s.includes('||')) {
          texts = s.split('||').map(v => v.trim()).filter(Boolean);
        } else {
          texts = [String(s)];
        }
      } else if (arrRaw.length > 1) {
        texts = arrRaw.map(t => String(t || ''));
      }
      // Debug: log parsed candidates preview
      try {
        const previews = texts.slice(0, 3).map(t => (t || '').replace(/\s+/g, ' ').slice(0, 120));
        console.log('[Overlay] onCandidates parsed', { count: texts.length, previews });
      } catch (_) {}
      const list = texts.slice(0, 10).map((t, i) => ({ id: Date.now() + '-' + i, text: t }));
      isLoading = false; lastError = null;
      lastGenerated = list; renderCandidates(list);
      updateSendEnabled();
    } catch (e) {
      lastError = '解析候选失败'; isLoading = false; renderLoading();
    }
  };

  window.__overlay_onCandidatesError = function(err) {
    try {
      const msg = (err && (err.message || err.code)) ? (err.message || err.code) : '生成失败';
      try { console.warn('[Overlay] onCandidatesError', err); } catch(_) {}
      isLoading = false; lastError = msg; renderLoading();
      updateSendEnabled();
      try { bridge.showToast && bridge.showToast(msg); } catch(_) {}
      try { bridge.hapticImpact && bridge.hapticImpact('medium'); } catch(_) {}
    } catch(_) {}
  };

  function maybeGenerate() {
    const text = inputEl?.value?.trim() || '';
    if (isLoading) return; // 防抖：生成中不重复发送
    if (!text) {
      lastGenerated = [];
      candidatesEl && (candidatesEl.innerHTML = '');
      return;
    }
    // 原生单次请求：将已选子类的单条 prompt（替换 {input}/{k}）直接发送给原生
    if (bridge.generateCandidates) {
      isLoading = true; lastError = null; lastGenerated = []; renderLoading();
      const k = 5;
      const finalPrompt = buildFinalPrompt(text, k);
      // Debug: log outgoing final prompt and options
      try {
        const catName = selectedCategory?.name || null;
        const subName = selectedSubcategory?.name || null;
        const preview = (finalPrompt || '').replace(/\s+/g, ' ').slice(0, 200);
        console.log('[Overlay] generateCandidates send', { k, cat: catName, sub: subName, promptLen: finalPrompt.length, promptPreview: preview });
      } catch (_) {}
      try { bridge.generateCandidates(finalPrompt, JSON.stringify({ k })); }
      catch (e) { window.__overlay_onCandidatesError({ code: 'bridge_error', message: String(e && e.message || e) }); }
      return;
    }
    // Fallback: 本地构造单条候选（不调用网络）
    const k = 5;
    const finalPrompt = buildFinalPrompt(text, k);
    try {
      const catName = selectedCategory?.name || null;
      const subName = selectedSubcategory?.name || null;
      const preview = (finalPrompt || '').replace(/\s+/g, ' ').slice(0, 200);
      console.log('[Overlay] fallback candidates', { k, cat: catName, sub: subName, promptLen: finalPrompt.length, promptPreview: preview });
    } catch (_) {}
    const list = [{ id: Date.now() + '-0', text: finalPrompt }];
    lastGenerated = list; renderCandidates(list);
  }

  function scheduleGenerate(immediate = false) {
    try { if (genTimer) { clearTimeout(genTimer); genTimer = null; } } catch(_) {}
    try { if (genIdle) { cIC(genIdle); genIdle = null; } } catch(_) {}
    if (immediate) return maybeGenerate();
    genTimer = setTimeout(() => { genIdle = rIC(() => maybeGenerate()); }, 120);
  }

  // 取消批量逻辑，保留 scheduleGenerate 供“重试/换一换”显式触发

  function updateActiveStates() {
    // 一级
    if (tabsRow) {
      const btns = tabsRow.querySelectorAll('.chip');
      btns.forEach(b => b.classList.remove('active'));
      if (selectedCategory) {
        // 找到与文本匹配的第一个按钮
        for (const b of btns) {
          if (b.textContent && selectedCategory && b.textContent.includes(selectedCategory.name || '')) {
            b.classList.add('active');
            break;
          }
        }
      }
    }
    // 二级
    if (subcatRow) {
      const cards = subcatRow.querySelectorAll('.subcard');
      cards.forEach(card => card.classList.remove('active'));
      if (selectedSubcategory) {
        for (const c of cards) {
          const title = c.querySelector('.title');
          if (title && title.textContent && title.textContent.includes(selectedSubcategory.name || '')) {
            c.classList.add('active');
            break;
          }
        }
      }
    }
  }

  function send(e) {
    if (e) {
      e.preventDefault();
      e.stopPropagation();
    }
    
    const text = inputEl?.value?.trim() || '';
    if (!text) return false;
    
    try {
      if (bridge.sendMessage) {
        bridge.sendMessage(text);
        // Clear input after sending
        if (inputEl) inputEl.value = '';
      }
    } catch (e) {
      console.error('Error sending message:', e);
      return false;
    }
    
    try { bridge.showToast && bridge.showToast('已发送'); } catch(_) {}
    try { bridge.hapticImpact && bridge.hapticImpact('light'); } catch(_) {}
    announce('已发送');
    
    return false;
  }

  // 关闭流程：先播放退出动效，再调用原生关闭
  let isClosing = false;
  function closeWithExit() {
    if (isClosing) return;
    isClosing = true;
    try {
      if (rootEl) {
        rootEl.classList.add('closing');
        rootEl.classList.remove('mounted');
      }
      const prefersReduced = (() => {
        try { return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; } catch(_) { return false; }
      })();
      const finish = () => { try { bridge.close && bridge.close(); } catch(_) {} };
      if (prefersReduced) {
        // 直接关闭或极短延迟
        return setTimeout(finish, 10);
      }
      let done = false;
      const onEnd = (ev) => {
        if (done) return; done = true;
        try { rootEl && rootEl.removeEventListener('transitionend', onEnd); } catch(_) {}
        finish();
      };
      try {
        rootEl && rootEl.addEventListener('transitionend', onEnd, { once: true });
        // 兜底：最长180ms
        setTimeout(onEnd, 200);
      } catch(_) { finish(); }
    } catch(_) {
      try { bridge.close && bridge.close(); } catch(__) {}
    }
  }

  closeBtn?.addEventListener('click', function(){
    closeWithExit();
  });

  pasteBtn?.addEventListener('click', function(){
    try {
      if (bridge.pasteFromClipboard) {
        const text = bridge.pasteFromClipboard();
        if (text && inputEl) {
          inputEl.value = text;
          inputEl.focus();
          updateSendEnabled();
          try { bridge.showToast && bridge.showToast('已粘贴'); } catch(_) {}
          try { bridge.hapticImpact && bridge.hapticImpact('light'); } catch(_) {}
          announce('已粘贴');
        }
      }
    } catch (e) {}
  });

  // 发送按钮事件在 initApp 中统一处理
  shuffleBtn?.addEventListener('click', function(){
    scheduleGenerate(true);
  });

  // 关闭按钮（已在上方绑定一次，这里去重）

  // 键盘快捷键：Ctrl/Cmd+Enter 发送，Esc 关闭
  inputEl?.addEventListener('keydown', (e) => {
    const isEnter = e.key === 'Enter' || e.keyCode === 13;
    if (isEnter && (e.ctrlKey || e.metaKey)) {
      e.preventDefault();
      // Ctrl/Cmd+Enter 优先生成候选；再次按或点击发送可唤起主应用
      maybeGenerate();
    } else if (e.key === 'Escape') {
      e.preventDefault();
      closeWithExit();
    }
  });

  // 输入变化时仅更新发送按钮可用状态与字数，不自动生成
  inputEl?.addEventListener('input', () => {
    
    // 添加字数统计
    const text = inputEl?.value || '';
    const charCount = text.length;
    // 可以在这里添加字数显示逻辑，例如更新某个元素的文本内容
    // 示例：document.getElementById('char-count').textContent = charCount;
    updateSendEnabled();
  });

  // 触发进场动画（在下一帧添加 mounted 类，确保过渡生效）
  try {
    window.requestAnimationFrame(() => { rootEl && rootEl.classList.add('mounted'); });
  } catch (e) {}

  // Ripple 效果：对所有 icon 按钮与关闭按钮生效（包含动态生成元素）
  function injectRipple(el, x, y) {
    try {
      const rect = el.getBoundingClientRect();
      const d = Math.max(rect.width, rect.height) * 1.2;
      const ink = document.createElement('span');
      ink.className = 'ripple-ink';
      ink.style.width = ink.style.height = d + 'px';
      const left = (typeof x === 'number' ? x : rect.width / 2) - d / 2;
      const top  = (typeof y === 'number' ? y : rect.height / 2) - d / 2;
      ink.style.left = left + 'px';
      ink.style.top = top + 'px';
      // 清理
      ink.addEventListener('animationend', () => { try { ink.remove(); } catch(_) {} }, { once: true });
      el.appendChild(ink);
    } catch(_) {}
  }

  document.addEventListener('pointerdown', (e) => {
    const el = e.target && (e.target.closest && e.target.closest('.icon-btn, #close-btn'));
    if (!el) return;
    if (prefersReduced) return; // 降低动效时跳过 ripple
    // 禁用按钮不触发ripple
    if (el.matches && el.matches(':disabled')) return;
    if (typeof el.disabled === 'boolean' && el.disabled) return;
    const rect = el.getBoundingClientRect();
    injectRipple(el, e.clientX - rect.left, e.clientY - rect.top);
  }, { passive: true });

  // 辅助方法：SR 实时播报
  function announce(msg) {
    try {
      if (!srLive) return;
      // 触发读屏：重置文本再赋值
      srLive.textContent = '';
      // 使用微任务确保DOM更新
      setTimeout(() => { try { srLive.textContent = msg; } catch(_) {} }, 0);
    } catch(_) {}
  }

  // 辅助方法：根据输入内容启用/禁用发送按钮
  function updateSendEnabled() {
    try {
      if (!sendBtn) return;
      const hasText = !!(inputEl && inputEl.value && inputEl.value.trim().length);
      sendBtn.disabled = !hasText;
    } catch(_) {}
  }

  // 初始化应用
  async function initApp() {
    try {
      // 加载AI配置
      loadAiConfigFromBridge();
      
      // 加载分类
      await loadCategoriesFromFile();
      
      // 初始化模型选择器
      initModelSelector();
      
      // 更新拖拽排除区域
      updateDragExclusions();
      
      // 初始状态下刷新一次发送按钮可用性
      updateSendEnabled();
      
      // 添加窗口大小变化监听
      window.addEventListener('resize', updateDragExclusions);
      
      // 添加粘贴按钮点击事件
      pasteBtn?.addEventListener('click', () => {
        navigator.clipboard.readText().then(text => {
          if (text) {
            inputEl.value = text;
            inputEl.focus();
            updateSendEnabled();
          }
        }).catch(console.error);
      });
      
      // 添加输入框输入事件
      inputEl?.addEventListener('input', updateSendEnabled);
      
      // 发送按钮点击事件 - 处理发送和生成候选
      sendBtn?.addEventListener('click', function(e) {
        if (e) {
          e.preventDefault();
          e.stopPropagation();
        }
        // 如果当前有选中的分类，则生成候选，否则发送消息
        if (selectedCategory || selectedSubcategory) {
          maybeGenerate();
        } else {
          send();
        }
      });
      
      // 添加回车键发送
      inputEl?.addEventListener('keydown', (e) => {
        if (e.key === 'Enter' && !e.shiftKey) {
          e.preventDefault();
          send();
        }
      });
      
      console.log('App initialized');
    } catch (error) {
      console.error('Failed to initialize app:', error);
    }
  }
  
  // 启动应用
  initApp();

})();
